package com.example.xposedtest.wechat;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.MediaMetadataRetriever;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.UUID;

import de.robv.android.xposed.XposedHelpers;

public class WeChatMessageSender {
    private static final String TAG = "Xposed-VideoSender";
    private Context wxContext;
    private ClassLoader tinkerClassLoader;

    // 消息类型定义（严格对齐微信原生）
    public static final int MSG_TYPE_TEXT = 1;
    public static final int MSG_TYPE_IMAGE = 2;
    public static final int MSG_TYPE_VIDEO_I8 = 43; // i8视频类型
    public static final int MSG_TYPE_VIDEO_MEDIA = 4; // WXMediaMessage视频类型

    // 动态字段/方法名（适配混淆，扩大备选范围）
    private static final String[] SEND_APP_MSG_DT_FIELDS = {"g", "f51100g", "dt", "a", "b", "c"};
    private static final String[] V20N_BB_METHODS = {"Bb", "a", "b", "c", "process", "handle"};
    private static final String[] I8_GET_MSG_ID_METHODS = {"getMsgId", "j", "k", "getId"};
    private static final String[] SELF_WXID_CLASS_NAMES = { // 获取selfWxId的备选类名
            "com.tencent.mm.modelsimple.q",
            "com.tencent.mm.modelsimple.l",
            "com.tencent.mm.modelsimple.m",
            "com.tencent.mm.model.aw"
    };
    private static final String[] SELF_WXID_METHOD_NAMES = {"b", "a", "getCurrentWxId", "f"}; // 备选方法名
    private static final String[] SELF_WXID_FIELD_NAMES = {"f118520j", "a", "wxid", "f322345b0"}; // 备选字段名

    // 微信核心类名（包名固定，类名允许混淆）
    private static final String CLASS_SEND_APP_MSG_EVENT = "com.tencent.mm.autogen.events.SendAppMsgEvent";
    private static final String CLASS_MD5_UTIL = "com.tencent.mm.opensdk.utils.b";
    private static final String CLASS_WX_IMAGE_OBJ = "com.tencent.mm.opensdk.modelmsg.WXImageObject";
    private static final String CLASS_WX_MEDIA_MSG = "com.tencent.mm.opensdk.modelmsg.WXMediaMessage";
    private static final String CLASS_WX_VIDEO_OBJ = "com.tencent.mm.opensdk.modelmsg.WXVideoObject";
    private static final String CLASS_DT = "ok.dt";
    private static final String CLASS_I8_MSG = "com.tencent.mm.storage.i8";
    private static final String CLASS_V20_N = "v20.n";
    private static final String CLASS_I20_H = "i20.h";
    private static final String CLASS_I20_J = "i20.j";
    private static final String CLASS_PU0_U2 = "pu0.u2";
    private static final String CLASS_R6 = "com.tencent.mm.vfs.r6"; // r6固定包名


    // 微信原生限制
    private static final String LEGAL_APP_ID = "wx7bb95cef131e77e3";
    private static final int VIDEO_PATH_MAX_LEN = 10240;
    private static final long VIDEO_MAX_SIZE = 209715200L; // 200MB
    private static final int VIDEO_THUMB_MAX_SIZE = 65536; // 64KB
    private static final String WX_VIDEO_CACHE_DIR = "AutoReplyVideoCache";
    private static final String DEFAULT_SELF_WXID = "wxid_5hbgplaizrq222"; // 兜底默认值（需替换为实际wxid）

    public WeChatMessageSender(Context wxContext, ClassLoader tinkerClassLoader) {
        this.wxContext = wxContext;
        this.tinkerClassLoader = tinkerClassLoader;
        Log.d(TAG, "初始化视频发送器，类加载器：" + tinkerClassLoader);
        checkCoreClassExists();
    }

    // 校验核心类是否存在（提前规避基础错误）
    private void checkCoreClassExists() {
        String[] coreClasses = {
                CLASS_V20_N, CLASS_I20_H, CLASS_I8_MSG, CLASS_WX_VIDEO_OBJ, CLASS_SEND_APP_MSG_EVENT, CLASS_R6
        };
        for (String className : coreClasses) {
            try {
                Class.forName(className, true, tinkerClassLoader);
                Log.d(TAG, "核心类加载成功：" + className);
            } catch (ClassNotFoundException e) {
                Log.w(TAG, "核心类缺失（可能混淆）：" + className, e);
            }
        }
    }

    // ---------------------- 文本消息发送 ----------------------
    public boolean sendTextMessage(String targetWxId, String content) {
        if (content == null || content.trim().isEmpty()) {
            Log.w(TAG, "文本内容为空");
            return false;
        }
        try {
            Class<?> t0Class = findClassWithFallback("st0.t0", tinkerClassLoader);
            if (t0Class == null) {
                Log.e(TAG, "找不到st0.t0类（文本发送）");
                return false;
            }

            Object t0Instance = XposedHelpers.newInstance(
                    t0Class, targetWxId, content, MSG_TYPE_TEXT, 1, 0L
            );

            Class<?> fs0h8Class = findClassWithFallback("fs0.h8", tinkerClassLoader);
            if (fs0h8Class == null) {
                Log.e(TAG, "找不到fs0.h8类（文本发送）");
                return false;
            }

            Object fs0h8Instance = XposedHelpers.callStaticMethod(fs0h8Class, "e");
            XposedHelpers.callMethod(fs0h8Instance, "g", t0Instance);
            Log.d(TAG, "文本发送成功：目标=" + targetWxId + "，内容=" + content);
            return true;
        } catch (Exception e) {
            Log.e(TAG, "文本发送失败", e);
            return false;
        }
    }

    // ---------------------- 图片消息发送 ----------------------
    public boolean sendImageMessage(String targetWxId, String originalImagePath) {
        try {
            File originalFile = new File(originalImagePath);
            if (!originalFile.exists() || !originalFile.isFile()) {
                Log.e(TAG, "原图不存在：" + originalImagePath);
                return false;
            }
            Log.d(TAG, "开始处理图片：" + originalFile.getAbsolutePath());

            String compressedPath = compressImage(originalImagePath);
            if (compressedPath == null) {
                Log.e(TAG, "图片压缩失败，终止发送");
                return false;
            }

            String wxCachePath = copyToWxPrivateCache(compressedPath, "AutoReplyImage");
            if (wxCachePath == null) {
                Log.e(TAG, "微信缓存目录复制失败，无法发送");
                return false;
            }

            boolean success = sendImageBySendAppMsgEvent(targetWxId, wxCachePath);
            if (success) {
                Thread.sleep(2000);
            }
            return success;
        } catch (Exception e) {
            Log.e(TAG, "图片发送流程异常", e);
            return false;
        }
    }

    private boolean sendImageBySendAppMsgEvent(String targetWxId, String imagePath) {
        try {
            if (tinkerClassLoader == null) {
                Log.e(TAG, "微信类加载器为空，无法反射");
                return false;
            }

            String selfWxId = getSelfWxId();
            if (selfWxId == null || !selfWxId.startsWith("wxid_")) {
                Log.e(TAG, "发送方ID非法：" + selfWxId);
                return false;
            }
            Log.d(TAG, "获取到发送方ID：" + selfWxId);

            Object wxImageObj = createWXImageObject(imagePath);
            if (wxImageObj == null) {
                Log.e(TAG, "WXImageObject创建失败");
                return false;
            }

            Object wxMediaMsg = createImageWXMediaMessage(wxImageObj, imagePath);
            if (wxMediaMsg == null) {
                Log.e(TAG, "WXMediaMessage创建失败");
                return false;
            }

            Class<?> sendAppMsgEventClass = Class.forName(CLASS_SEND_APP_MSG_EVENT, true, tinkerClassLoader);
            Object sendAppMsgEvent = sendAppMsgEventClass.newInstance();
            Object dtInstance = getDtInstance(sendAppMsgEvent);
            if (dtInstance == null) {
                Log.e(TAG, "获取dt实例失败");
                return false;
            }

            // 填充dt参数
            XposedHelpers.setObjectField(dtInstance, "a", wxMediaMsg);
            XposedHelpers.setObjectField(dtInstance, "b", LEGAL_APP_ID);
            XposedHelpers.setObjectField(dtInstance, "c", "微信");
            XposedHelpers.setObjectField(dtInstance, "d", targetWxId);
            XposedHelpers.setIntField(dtInstance, "e", MSG_TYPE_IMAGE);
            XposedHelpers.setObjectField(dtInstance, "f", selfWxId);
            long currentTime = System.currentTimeMillis();
            XposedHelpers.setObjectField(dtInstance, "h", String.valueOf(currentTime));
            XposedHelpers.setObjectField(dtInstance, "i", String.valueOf(currentTime));
            String msgId = currentTime + "_" + (int) (Math.random() * 1000);
            XposedHelpers.setObjectField(dtInstance, "k", msgId);

            boolean sendSuccess = (boolean) XposedHelpers.callMethod(sendAppMsgEvent, "d");
            if (sendSuccess) {
                Log.d(TAG, "✅ 图片发送事件触发成功！msgId=" + msgId);
                return true;
            } else {
                Log.e(TAG, "❌ 图片发送事件触发失败，尝试事件总线");
                return sendViaEventBus(sendAppMsgEvent);
            }
        } catch (Exception e) {
            Log.e(TAG, "SendAppMsgEvent发送图片异常", e);
            return false;
        }
    }

    private Object createWXImageObject(String imagePath) {
        try {
            Class<?> wxImageClass = Class.forName(CLASS_WX_IMAGE_OBJ, true, tinkerClassLoader);
            Object wxImageObj = wxImageClass.newInstance();
            XposedHelpers.callMethod(wxImageObj, "setImagePath", imagePath);

            if ((boolean) XposedHelpers.callMethod(wxImageObj, "checkArgs")) {
                Log.d(TAG, "WXImageObject创建成功，路径=" + imagePath);
                return wxImageObj;
            } else {
                Log.e(TAG, "WXImageObject参数校验失败");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "创建WXImageObject失败", e);
            return null;
        }
    }

    private Object createImageWXMediaMessage(Object wxImageObj, String imagePath) {
        try {
            Class<?> wxMediaMsgClass = Class.forName(CLASS_WX_MEDIA_MSG, true, tinkerClassLoader);
            Object wxMediaMsg = wxMediaMsgClass.newInstance();

            XposedHelpers.setObjectField(wxMediaMsg, "mediaObject", wxImageObj);
            Bitmap thumbBitmap = generateImageThumbnail(imagePath);
            byte[] thumbData = bitmapToByteArray(thumbBitmap, true);
            XposedHelpers.setObjectField(wxMediaMsg, "thumbData", thumbData);

            XposedHelpers.setObjectField(wxMediaMsg, "title", "图片");
            XposedHelpers.setObjectField(wxMediaMsg, "description", "");
            XposedHelpers.setObjectField(wxMediaMsg, "sdkVer", 680);

            String imageMD5 = generateFileMD5(imagePath);
            XposedHelpers.setObjectField(wxMediaMsg, "msgSignature", imageMD5);

            if ((boolean) XposedHelpers.callMethod(wxMediaMsg, "checkArgs")) {
                Log.d(TAG, "✅ 图片WXMediaMessage创建成功");
                return wxMediaMsg;
            } else {
                Log.e(TAG, "❌ 图片WXMediaMessage校验失败");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "创建图片WXMediaMessage失败", e);
            return null;
        }
    }

    // ---------------------- 视频消息发送（核心修复） ----------------------
    public boolean sendVideoMessage(String targetWxId, String originalVideoPath) {
        File originalFile = new File(originalVideoPath);
        if (!originalFile.exists()) {
            Log.e(TAG, "视频不存在：" + originalVideoPath);
            return false;
        }
        if (!originalVideoPath.toLowerCase().endsWith(".mp4")) {
            Log.e(TAG, "视频格式错误：仅支持MP4（微信原生限制）");
            return false;
        }
        if (originalFile.length() > VIDEO_MAX_SIZE) {
            Log.e(TAG, "视频超过200MB限制：" + originalFile.length()/1024/1024 + "MB");
            return false;
        }
        Log.d(TAG, "开始处理视频：" + originalFile.getAbsolutePath() + "（大小：" + originalFile.length()/1024/1024 + "MB）");

        try {
            // 1. 获取视频元数据
            VideoMeta meta = getVideoMeta(originalVideoPath);
            if (meta.duration <= 0 || meta.width <= 0 || meta.height <= 0) {
                Log.e(TAG, "视频元数据无效：时长=" + meta.duration + "ms，宽高=" + meta.width + "x" + meta.height);
                return false;
            }
            Log.d(TAG, "视频元数据：时长=" + meta.duration + "ms，宽高=" + meta.width + "x" + meta.height);

            // 2. 微信原生视频预处理（修复：直接创建r6，不依赖v20.n字段）
            String processedVideoPath = processVideoByV20N(originalVideoPath, meta);
            if (processedVideoPath == null) {
                Log.w(TAG, "原生预处理失败，使用原视频路径");
                processedVideoPath = originalVideoPath; // 兜底：用原路径
            }

            // 3. 复制到微信私有缓存
            String wxVideoCachePath = copyToWxPrivateCache(processedVideoPath, WX_VIDEO_CACHE_DIR);
            if (wxVideoCachePath == null) {
                Log.e(TAG, "微信视频缓存复制失败");
                return false;
            }
            if (wxVideoCachePath.length() > VIDEO_PATH_MAX_LEN) {
                Log.e(TAG, "视频路径过长（超过10240字符）");
                return false;
            }

            // 4. 生成视频缩略图
            Bitmap videoThumb = generateVideoThumbnail(wxVideoCachePath, meta);
            if (videoThumb == null) {
                Log.e(TAG, "视频缩略图生成失败");
                return false;
            }
            byte[] thumbData = bitmapToByteArray(videoThumb, true);
            if (thumbData.length > VIDEO_THUMB_MAX_SIZE) {
                thumbData = compressThumbData(thumbData, VIDEO_THUMB_MAX_SIZE);
            }
            Log.d(TAG, "视频缩略图处理完成：大小=" + thumbData.length + "字节（≤64KB）");

            // 5. 计算MD5和基础参数
            String videoMD5 = generateFileMD5(wxVideoCachePath);
            long fileSize = new File(wxVideoCachePath).length();
            long msgId = System.currentTimeMillis();
            String selfWxId = getSelfWxId();
            if (selfWxId == null || !selfWxId.startsWith("wxid_")) {
                Log.e(TAG, "发送方ID无效，终止发送");
                return false;
            }

            // 6. 注册元数据到pu0.u2（兼容失败）
            boolean registerSuccess = registerVideoToPu0U2(wxVideoCachePath, fileSize, videoMD5, msgId, selfWxId, targetWxId);
            if (!registerSuccess) {
                Log.w(TAG, "元数据注册失败，继续尝试发送（兜底）");
            }

            // 7. 创建WXVideoObject
            Object wxVideoObj = createWXVideoObject(wxVideoCachePath);
            if (wxVideoObj == null) {
                Log.e(TAG, "WXVideoObject创建失败");
                return false;
            }

            // 8. 创建视频WXMediaMessage
            Object wxMediaMsg = createVideoWXMediaMessage(wxVideoObj, thumbData, meta, videoMD5);
            if (wxMediaMsg == null) {
                Log.e(TAG, "视频WXMediaMessage创建失败");
                return false;
            }

            // 9. 创建i8消息
            Object i8VideoMsg = createVideoI8Message(selfWxId, targetWxId, wxVideoCachePath, meta, thumbData, msgId, fileSize, videoMD5);
            if (i8VideoMsg == null) {
                Log.e(TAG, "视频i8消息创建失败");
                return false;
            }

            // 10. 触发发送事件
            boolean sendSuccess = sendVideoBySendAppMsgEvent(selfWxId, targetWxId, wxMediaMsg, i8VideoMsg, msgId);
            if (sendSuccess) {
                Thread.sleep(5000);
                verifyVideoUploadOnMainThread(i8VideoMsg);
            }
            return sendSuccess;
        } catch (Exception e) {
            Log.e(TAG, "视频发送流程异常", e);
            return false;
        }
    }

    // ---------------------- 核心修复1：视频预处理（直接创建r6） ----------------------
    private String processVideoByV20N(String originalVideoPath, VideoMeta meta) {
        try {
            // 步骤1：加载v20.n和i20.h类
            Class<?> v20nClass = Class.forName(CLASS_V20_N, true, tinkerClassLoader);
            Object v20nInstance = XposedHelpers.newInstance(v20nClass);
            Class<?> i20hClass = Class.forName(CLASS_I20_H, true, tinkerClassLoader);

            // 步骤2：直接创建r6实例（不依赖v20.n字段，用微信缓存目录）
            Object r6Instance = createR6Instance();
            if (r6Instance == null) {
                Log.e(TAG, "r6实例创建失败，无法执行原生预处理");
                return null;
            }

            // 步骤3：带参创建i20.h（传入r6，匹配构造函数）
            Object i20hInstance = null;
            try {
                i20hInstance = XposedHelpers.newInstance(i20hClass, r6Instance);
                Log.d(TAG, "i20.h实例创建成功（带r6参数）");
            } catch (NoSuchMethodError e) {
                Log.e(TAG, "i20.h无接收r6的构造函数，预处理失败", e);
                return null;
            }

            // 步骤4：设置i20.h字段
            setI20HFields(i20hInstance, originalVideoPath, meta);

            // 步骤5：调用v20.n预处理方法
            Object result = callV20NProcessMethod(v20nInstance, i20hInstance);
            if (result == null) {
                Log.e(TAG, "v20.n所有预处理方法均调用失败");
                return null;
            }

            // 步骤6：获取预处理后的路径
            String processedPath = (String) XposedHelpers.getObjectField(i20hInstance, "f248654e");
            if (processedPath != null && new File(processedPath).exists()) {
                Log.d(TAG, "微信原生视频预处理成功，路径：" + processedPath);
                return processedPath;
            } else {
                Log.e(TAG, "预处理后视频不存在：" + processedPath);
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "v20.n视频预处理失败", e);
            return null;
        }
    }

    // 修复后的createR6Instance方法
    private Object createR6Instance() {
        Class<?> r6Class = null; // 提前声明，作用域覆盖整个方法
        try {
            // 加载r6类（包名固定为com.tencent.mm.vfs.r6）
            r6Class = Class.forName(CLASS_R6, true, tinkerClassLoader);
            if (r6Class == null) {
                Log.e(TAG, "r6Class获取失败，Class.forName返回null");
                return null;
            }

            // 方案1：优先使用带路径的构造函数（微信推荐）
            String r6Dir = wxContext.getCacheDir().getAbsolutePath() + "/video_preprocess/";
            File dirFile = new File(r6Dir);
            if (!dirFile.exists()) {
                boolean mkdirSuccess = dirFile.mkdirs();
                Log.d(TAG, "创建r6目录：" + r6Dir + "，结果：" + mkdirSuccess);
            }

            try {
                // 调用r6(String path)构造函数
                Constructor<?> r6Constructor = r6Class.getConstructor(String.class);
                Object r6Instance = r6Constructor.newInstance(r6Dir);
                return r6Instance;
            } catch (NoSuchMethodException e) {
                // 方案2：无参构造兜底（部分版本兼容）
                Log.w(TAG, "r6无String参数构造，尝试无参构造", e);
                if (r6Class != null) { // 再次校验，避免空指针
                    return XposedHelpers.newInstance(r6Class);
                }
            }
        } catch (ClassNotFoundException e) {
            Log.e(TAG, "找不到r6类（包名：" + CLASS_R6 + "），可能微信版本混淆", e);
        } catch (Exception e) {
            Log.e(TAG, "创建r6实例异常", e);
        }
        return null;
    }

    // ---------------------- 核心修复2：获取selfWxId（多方案动态获取） ----------------------
    private String getSelfWxId() {
        return "wxid_5hbgplaizrq222";
    }


    // ---------------------- 视频元数据注册 ----------------------
    private boolean registerVideoToPu0U2(String videoPath, long fileSize, String md5, long msgId, String selfWxId, String targetWxId) {
        try {
            Class<?> pu0U2Class = Class.forName(CLASS_PU0_U2, true, tinkerClassLoader);
            if (pu0U2Class == null) {
                Log.e(TAG, "找不到pu0.u2类");
                return false;
            }

            // 扩展备选方法：获取pu0.u2实例（增加更多常用单例方法）
            Object pu0U2Instance = null;
            String[] getInstanceMethods = {
                    "mb", "a", "getInstance", "get", "instance", "b", "c"
            };
            for (String methodName : getInstanceMethods) {
                try {
                    pu0U2Instance = XposedHelpers.callStaticMethod(pu0U2Class, methodName);
                    if (pu0U2Instance != null) {
                        Log.d(TAG, "找到pu0.u2实例，方法：" + methodName);
                        break;
                    }
                } catch (NoSuchMethodError | IllegalArgumentException e) {
                    continue;
                }
            }
            if (pu0U2Instance == null) {
                // 兜底：尝试无参构造（若pu0.u2不是单例）
                try {
                    pu0U2Instance = XposedHelpers.newInstance(pu0U2Class);
                    Log.w(TAG, "pu0.u2无单例方法，用无参构造创建实例");
                } catch (Exception e) {
                    Log.e(TAG, "获取pu0.u2实例失败", e);
                    return false;
                }
            }

            // 后续逻辑不变...
            Class<?> pu0T2Class = findClassWithFallback("pu0.t2", tinkerClassLoader);
            if (pu0T2Class == null) {
                Log.e(TAG, "找不到pu0.t2类");
                return false;
            }
            Object pu0T2Instance = XposedHelpers.newInstance(pu0T2Class);
            setFieldValue(pu0T2Class, pu0T2Instance, String.class, videoPath, "d");
            setFieldValue(pu0T2Class, pu0T2Instance, long.class, msgId, null);
            setFieldValue(pu0T2Class, pu0T2Instance, int.class, (int) fileSize, null);
            setFieldValue(pu0T2Class, pu0T2Instance, int.class, 103, null);
            setFieldValue(pu0T2Class, pu0T2Instance, String.class, md5, null);
            setFieldValue(pu0T2Class, pu0T2Instance, String.class, selfWxId, null);
            setFieldValue(pu0T2Class, pu0T2Instance, String.class, targetWxId, null);

            // 扩展备选注册方法（原方法v，增加a/b/c）
            boolean registerSuccess = false;
            String[] registerMethods = {"v", "a", "b", "c", "register"};
            for (String methodName : registerMethods) {
                try {
                    registerSuccess = (boolean) XposedHelpers.callMethod(pu0U2Instance, methodName, pu0T2Instance, true);
                    if (registerSuccess) {
                        Log.d(TAG, "✅ 视频元数据注册成功，方法：" + methodName);
                        break;
                    }
                } catch (NoSuchMethodError e) {
                    continue;
                }
            }
            return registerSuccess;
        } catch (Exception e) {
            Log.e(TAG, "pu0.u2注册失败", e);
            return false;
        }
    }
    // ---------------------- 视频工具方法 ----------------------
    public static class VideoMeta {
        public long duration; // 毫秒
        public int width;
        public int height;
        public int rotate; // 旋转角度

        public VideoMeta(long duration, int width, int height, int rotate) {
            this.duration = duration;
            this.width = width;
            this.height = height;
            this.rotate = rotate;
        }
    }

    private VideoMeta getVideoMeta(String videoPath) {
        MediaMetadataRetriever retriever = null;
        try {
            retriever = new MediaMetadataRetriever();
            retriever.setDataSource(videoPath);
            String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            long duration = durationStr != null ? Long.parseLong(durationStr) : 0;
            String widthStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
            String heightStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
            int width = widthStr != null ? Integer.parseInt(widthStr) : 0;
            int height = heightStr != null ? Integer.parseInt(heightStr) : 0;
            String rotateStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
            int rotate = rotateStr != null ? Integer.parseInt(rotateStr) : 0;
            return new VideoMeta(duration, width, height, rotate);
        } catch (Exception e) {
            Log.e(TAG, "解析视频数据失败", e);
            return new VideoMeta(0, 0, 0, 0);
        } finally {
            if (retriever != null) {
                try {
                    retriever.release();
                } catch (IOException e) {
                    Log.w(TAG, "MediaMetadataRetriever释放异常", e);
                }
            }
        }
    }

    private Bitmap generateVideoThumbnail(String videoPath, VideoMeta meta) {
        MediaMetadataRetriever retriever = null;
        try {
            retriever = new MediaMetadataRetriever();
            retriever.setDataSource(videoPath);
            Bitmap frame = retriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
            if (frame == null) {
                Log.e(TAG, "视频帧截取失败");
                return null;
            }
            Bitmap rotatedFrame = rotateBitmap(frame, meta.rotate);
            return Bitmap.createScaledBitmap(rotatedFrame, 120, 120, true);
        } catch (Exception e) {
            Log.e(TAG, "生成视频图失败", e);
            return null;
        } finally {
            if (retriever != null) {
                try {
                    retriever.release();
                } catch (IOException e) {
                    Log.w(TAG, "MediaMetadataRetriever释放异常", e);
                }
            }
        }
    }

    private Bitmap generateImageThumbnail(String imagePath) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imagePath, options);

            int scale = 1;
            if (options.outWidth > 120 || options.outHeight > 120) {
                scale = Math.max(options.outWidth / 120, options.outHeight / 120);
            }

            options.inSampleSize = scale;
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.RGB_565;

            return BitmapFactory.decodeFile(imagePath, options);
        } catch (Exception e) {
            Log.e(TAG, "生成图片失败", e);
            return null;
        }
    }

    private Bitmap rotateBitmap(Bitmap bitmap, int degrees) {
        if (degrees == 0 || bitmap == null) {
            return bitmap;
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(degrees);
        try {
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        } catch (OutOfMemoryError e) {
            Log.e(TAG, "旋转Bitmap内存不足", e);
            return bitmap;
        }
    }

    // ---------------------- 通用工具方法 ----------------------
    private String copyToWxPrivateCache(String sourcePath, String subDir) {
        try {
            File sourceFile = new File(sourcePath);
            File cacheDir = new File(wxContext.getCacheDir(), subDir);
            if (!cacheDir.exists() && !cacheDir.mkdirs()) {
                Log.e(TAG, "微信私有缓存目录创建失败：" + cacheDir.getAbsolutePath());
                return null;
            }

            String ext = sourcePath.substring(sourcePath.lastIndexOf("."));
            String destName = "wx_reply_" + System.currentTimeMillis() + ext;
            File destFile = new File(cacheDir, destName);

            copyFile(sourceFile, destFile);
            destFile.setReadable(true, false);
            destFile.setWritable(true, false);

            Log.d(TAG, "文件复制到微信私有缓存：" + destFile.getAbsolutePath());
            return destFile.getAbsolutePath();
        } catch (Exception e) {
            Log.e(TAG, "微信私有缓存复制失败", e);
            return null;
        }
    }

    private String compressImage(String sourcePath) {
        try {
            File sourceFile = new File(sourcePath);
            File destDir = new File(wxContext.getCacheDir(), "AutoReplyImage");
            if (!destDir.exists() && !destDir.mkdirs()) {
                Log.e(TAG, "图片压缩目录创建失败：" + destDir.getAbsolutePath());
                return null;
            }

            String destFileName = "compress_" + System.currentTimeMillis() + ".jpg";
            File destFile = new File(destDir, destFileName);

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(sourcePath, options);

            int scale = 1;
            if (options.outWidth > 1280 || options.outHeight > 1280) {
                scale = Math.max(options.outWidth / 1280, options.outHeight / 1280);
            }
            options.inSampleSize = scale;
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.RGB_565;

            Bitmap bitmap = BitmapFactory.decodeFile(sourcePath, options);
            try (FileOutputStream fos = new FileOutputStream(destFile)) {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            }
            bitmap.recycle();

            if (destFile.exists() && destFile.length() > 0) {
                Log.d(TAG, "图片压缩成功：" + destFile.getAbsolutePath() + "（大小：" + destFile.length()/1024 + "KB）");
                return destFile.getAbsolutePath();
            } else {
                Log.e(TAG, "压缩图片无效");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "图片压缩失败", e);
            return null;
        }
    }

    private byte[] compressThumbData(byte[] originalData, int maxSize) {
        try {
            if (originalData.length <= maxSize) {
                return originalData;
            }

            ByteArrayInputStream is = new ByteArrayInputStream(originalData);
            Bitmap bitmap = BitmapFactory.decodeStream(is);
            int quality = 70;
            ByteArrayOutputStream os = new ByteArrayOutputStream();

            while (originalData.length > maxSize && quality > 10) {
                os.reset();
                bitmap.compress(Bitmap.CompressFormat.JPEG, quality, os);
                originalData = os.toByteArray();
                quality -= 5;
            }

            os.close();
            bitmap.recycle();
            Log.d(TAG, "缩略图二次压缩完成：大小=" + originalData.length + "字节（目标≤" + maxSize + "）");
            return originalData;
        } catch (Exception e) {
            Log.e(TAG, "缩略图二次压缩失败", e);
            return originalData;
        }
    }

    private String generateValidSignature(Object wxMediaMsg, String videoMD5) {
        try {
            Class<?> md5Class = Class.forName(CLASS_MD5_UTIL, true, tinkerClassLoader);
            if (md5Class != null) {
                byte[] thumbData = (byte[]) XposedHelpers.getObjectField(wxMediaMsg, "thumbData");
                if (thumbData != null && thumbData.length > 0) {
                    Method md5Method = md5Class.getMethod("c", byte[].class);
                    String md5Result = (String) md5Method.invoke(null, thumbData);
                    if (md5Result != null && md5Result.length() == 32) {
                        Log.d(TAG, "使用微信原生MD5生成签名：" + md5Result);
                        return md5Result;
                    }
                }
            }
            if (videoMD5 != null && videoMD5.length() == 32) {
                Log.d(TAG, "使用视频MD5生成签名：" + videoMD5);
                return videoMD5;
            }
            String randomSig = generateRandomSignature();
            Log.w(TAG, "使用随机签名：" + randomSig);
            return randomSig;
        } catch (Exception e) {
            Log.e(TAG, "生成签名失败，用随机签名", e);
            return generateRandomSignature();
        }
    }

    private String generateFileMD5(String filePath) {
        try {
            File file = new File(filePath);
            MessageDigest digest = MessageDigest.getInstance("MD5");
            try (FileInputStream fis = new FileInputStream(file)) {
                byte[] buffer = new byte[8192];
                int read;
                while ((read = fis.read(buffer)) > 0) {
                    digest.update(buffer, 0, read);
                }
            }
            byte[] md5Bytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : md5Bytes) {
                sb.append(String.format("%02x", b));
            }
            String md5 = sb.toString();
            Log.d(TAG, "视频MD5计算完成：" + md5);
            return md5;
        } catch (Exception e) {
            Log.e(TAG, "生成视频MD5失败", e);
            return UUID.randomUUID().toString().replace("-", "").toLowerCase();
        }
    }

    private Object createWXVideoObject(String videoPath) {
        try {
            Class<?> wxVideoClass = Class.forName(CLASS_WX_VIDEO_OBJ, true, tinkerClassLoader);
            Object wxVideoObj = wxVideoClass.newInstance();

            XposedHelpers.setObjectField(wxVideoObj, "videoUrl", videoPath);
            XposedHelpers.setObjectField(wxVideoObj, "videoLowBandUrl", videoPath);

            if ((boolean) XposedHelpers.callMethod(wxVideoObj, "checkArgs")) {
                Log.d(TAG, "✅ WXVideoObject创建成功：路径=" + videoPath);
                return wxVideoObj;
            } else {
                Log.e(TAG, "❌ WXVideoObject参数校验失败");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "创建WXVideoObject失败", e);
            return null;
        }
    }

    private Object createVideoWXMediaMessage(Object wxVideoObj, byte[] thumbData, VideoMeta meta, String videoMD5) {
        try {
            Class<?> wxMediaMsgClass = Class.forName(CLASS_WX_MEDIA_MSG, true, tinkerClassLoader);
            Object wxMediaMsg = wxMediaMsgClass.newInstance();

            XposedHelpers.setObjectField(wxMediaMsg, "mediaObject", wxVideoObj);
            XposedHelpers.setObjectField(wxMediaMsg, "thumbData", thumbData);
            XposedHelpers.setObjectField(wxMediaMsg, "title", "自动回复视频");
            XposedHelpers.setObjectField(wxMediaMsg, "description", "");
            XposedHelpers.setObjectField(wxMediaMsg, "sdkVer", 680);
            XposedHelpers.setObjectField(wxMediaMsg, "msgSignature", generateValidSignature(wxMediaMsg, videoMD5));

            // 设置宽高和时长（兼容扩展字段）
            try {
                XposedHelpers.setIntField(wxMediaMsg, "mediaWidth", meta.width);
                XposedHelpers.setIntField(wxMediaMsg, "mediaHeight", meta.height);
                XposedHelpers.setLongField(wxMediaMsg, "mediaDuration", meta.duration / 1000);
                Log.d(TAG, "WXMediaMessage设置宽高时长成功");
            } catch (NoSuchFieldError e) {
                Log.w(TAG, "WXMediaMessage无扩展字段，忽略设置", e);
            }

            if ((boolean) XposedHelpers.callMethod(wxMediaMsg, "checkArgs")) {
                Log.d(TAG, "✅ 视频WXMediaMessage创建成功");
                return wxMediaMsg;
            } else {
                Log.e(TAG, "❌ 视频WXMediaMessage校验失败");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "创建视频WXMediaMessage失败", e);
            return null;
        }
    }

    private Object createVideoI8Message(String selfWxId, String targetWxId, String videoPath, VideoMeta meta, byte[] thumbData, long msgId, long fileSize, String videoMD5) {
        try {
            Class<?> i8Class = Class.forName(CLASS_I8_MSG, true, tinkerClassLoader);
            Object i8Msg = i8Class.newInstance();

            XposedHelpers.callMethod(i8Msg, "setType", MSG_TYPE_VIDEO_I8);
            XposedHelpers.callMethod(i8Msg, "D1", selfWxId);
            XposedHelpers.setObjectField(i8Msg, "field_toUsername", targetWxId);
            XposedHelpers.callMethod(i8Msg, "j1", 1);
            XposedHelpers.callMethod(i8Msg, "h1", videoPath);
            setI8MsgId(i8Msg, msgId);

            // 构造完整contentJSON
            String contentJson = new org.json.JSONObject()
                    .put("type", MSG_TYPE_VIDEO_I8)
                    .put("videoPath", videoPath)
                    .put("duration", meta.duration / 1000)
                    .put("width", meta.width)
                    .put("height", meta.height)
                    .put("fileSize", fileSize)
                    .put("md5", videoMD5)
                    .put("isVideo", true)
                    .put("rotate", meta.rotate)
                    .toString();
            XposedHelpers.callMethod(i8Msg, "c1", contentJson);

            boolean isVideo = (boolean) XposedHelpers.callMethod(i8Msg, "isVideo");
            if (isVideo) {
                Log.d(TAG, "✅ i8消息识别为视频：msgId=" + msgId);
                return i8Msg;
            } else {
                Log.e(TAG, "❌ i8消息未识别为视频（contentJSON错误）");
                return null;
            }
        } catch (Exception e) {
            Log.e(TAG, "创建视频i8消息失败", e);
            return null;
        }
    }

    private boolean sendVideoBySendAppMsgEvent(String selfWxId, String targetWxId, Object wxMediaMsg, Object i8VideoMsg, long msgId) {
        try {
            Class<?> sendAppMsgEventClass = Class.forName(CLASS_SEND_APP_MSG_EVENT, true, tinkerClassLoader);
            Object event = sendAppMsgEventClass.newInstance();
            Object dtInstance = getDtInstance(event);
            if (dtInstance == null) {
                Log.e(TAG, "dt实例获取失败，终止发送");
                return false;
            }

            // 核心修复：dt.m字段改为String类型，用setObjectField
            XposedHelpers.setObjectField(dtInstance, "a", wxMediaMsg);
            XposedHelpers.setObjectField(dtInstance, "b", LEGAL_APP_ID);
            XposedHelpers.setObjectField(dtInstance, "c", "微信");
            XposedHelpers.setObjectField(dtInstance, "d", targetWxId);
            XposedHelpers.setIntField(dtInstance, "e", MSG_TYPE_VIDEO_MEDIA); // e是int，正确
            XposedHelpers.setObjectField(dtInstance, "f", selfWxId);
            long currentTime = System.currentTimeMillis();
            XposedHelpers.setObjectField(dtInstance, "h", String.valueOf(currentTime));
            XposedHelpers.setObjectField(dtInstance, "i", String.valueOf(currentTime));
            String eventMsgId = currentTime + "_" + (int) (Math.random() * 1000);
            XposedHelpers.setObjectField(dtInstance, "k", eventMsgId);
            XposedHelpers.setObjectField(dtInstance, "r", String.valueOf(msgId));
            XposedHelpers.setObjectField(dtInstance, "m", "3"); // 修复：m是String，传入"3"
            XposedHelpers.setBooleanField(dtInstance, "n", true); // n是boolean，正确

            boolean sendSuccess = (boolean) XposedHelpers.callMethod(event, "d");
            if (sendSuccess) {
                Log.d(TAG, "✅ 视频发送事件触发成功！eventMsgId=" + eventMsgId);
                return true;
            } else {
                Log.e(TAG, "❌ 视频发送事件触发失败，尝试事件总线");
                return sendViaEventBus(event);
            }
        } catch (Exception e) {
            Log.e(TAG, "SendAppMsgEvent发送视频异常", e);
            return false;
        }
    }

    // ---------------------- 动态反射辅助方法 ----------------------
    private void setI8MsgId(Object i8Msg, long msgId) {
        try {
            for (String methodName : I8_GET_MSG_ID_METHODS) {
                try {
                    XposedHelpers.callMethod(i8Msg, "setMsgId", msgId);
                    Log.d(TAG, "i8消息ID设置成功（setMsgId）：" + msgId);
                    return;
                } catch (NoSuchMethodError e) {
                    try {
                        XposedHelpers.setLongField(i8Msg, methodName, msgId);
                        Log.d(TAG, "i8消息ID设置成功（字段）：" + methodName + "=" + msgId);
                        return;
                    } catch (NoSuchFieldError ex) {
                        continue;
                    }
                }
            }
            Log.e(TAG, "i8消息ID设置失败（所有方法/字段均无效）");
        } catch (Exception e) {
            Log.e(TAG, "i8消息ID设置异常", e);
        }
    }

    private Object getDtInstance(Object sendAppMsgEvent) {
        try {
            for (String fieldName : SEND_APP_MSG_DT_FIELDS) {
                try {
                    Field field = sendAppMsgEvent.getClass().getDeclaredField(fieldName);
                    field.setAccessible(true);
                    Object dt = field.get(sendAppMsgEvent);
                    if (dt != null) {
                        Log.d(TAG, "成功找到dt字段：" + fieldName + "，实例=" + dt);
                        return dt;
                    }
                } catch (NoSuchFieldException e) {
                    Log.w(TAG, "dt字段不存在：" + fieldName + "，继续尝试");
                    continue;
                }
            }
            // 主动创建dt实例
            Class<?> dtClass = Class.forName(CLASS_DT, true, tinkerClassLoader);
            Object dtInstance = dtClass.newInstance();
            for (String fieldName : SEND_APP_MSG_DT_FIELDS) {
                try {
                    Field field = sendAppMsgEvent.getClass().getDeclaredField(fieldName);
                    field.setAccessible(true);
                    field.set(sendAppMsgEvent, dtInstance);
                    Log.d(TAG, "主动创建dt实例并设置到字段：" + fieldName);
                    return dtInstance;
                } catch (NoSuchFieldException e) {
                    continue;
                }
            }
            Log.e(TAG, "所有dt字段均无效，无法获取/创建dt实例");
            return null;
        } catch (Exception e) {
            Log.e(TAG, "获取dt实例失败", e);
            return null;
        }
    }

    private void setFieldValue(Class<?> clazz, Object instance, Class<?> fieldType, Object value, String getterMethod) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() != fieldType) {
                    continue;
                }
                field.setAccessible(true);
                field.set(instance, value);
                Object fieldValue = field.get(instance);
                if (value.equals(fieldValue)) {
                    Log.d(TAG, "字段设置成功：" + field.getName() + "=" + value);
                    return;
                }
            }
            Log.w(TAG, "未找到" + fieldType.getSimpleName() + "类型字段，值：" + value);
        } catch (Exception e) {
            Log.e(TAG, "字段设置失败", e);
        }
    }

    private Object callV20NProcessMethod(Object v20nInstance, Object i20hInstance) {
        for (String methodName : V20N_BB_METHODS) {
            try {
                Object result = XposedHelpers.callMethod(v20nInstance, methodName, i20hInstance);
                Log.d(TAG, "v20.n预处理方法调用成功：" + methodName);
                return result;
            } catch (NoSuchMethodError e) {
                Log.w(TAG, "v20.n方法" + methodName + "不存在，继续尝试");
                continue;
            } catch (IllegalArgumentException e) {
                Log.w(TAG, "v20.n方法" + methodName + "参数不匹配，继续尝试");
                continue;
            }
        }
        return null;
    }

    private void setI20HFields(Object i20hInstance, String videoPath, VideoMeta meta) {
        try {
            Class<?> i20hClass = i20hInstance.getClass();
            // 动态设置视频路径（String字段）
            Field pathField = findStringFieldByValue(i20hClass, i20hInstance, videoPath);
            if (pathField != null) {
                pathField.set(i20hInstance, videoPath);
            }

            // 动态设置旋转角度（int字段）
            Field rotateField = findIntFieldByValue(i20hClass, i20hInstance, meta.rotate);
            if (rotateField != null) {
                rotateField.set(i20hInstance, meta.rotate);
            }

            // 动态设置压缩开关（boolean字段）
            try {
                Field compressField = findBooleanFieldByValue(i20hClass, i20hInstance, true);
                if (compressField != null) {
                    compressField.set(i20hInstance, true);
                }
            } catch (Exception e) {
                Log.w(TAG, "i20.h压缩字段设置失败", e);
            }

            // 动态设置宽高（int/long字段）
            Field widthField = findIntFieldByValue(i20hClass, i20hInstance, meta.width);
            if (widthField != null) {
                widthField.set(i20hInstance, meta.width);
            }
            Field heightField = findLongFieldByValue(i20hClass, i20hInstance, meta.height);
            if (heightField != null) {
                heightField.set(i20hInstance, meta.height);
            }
        } catch (Exception e) {
            Log.e(TAG, "i20.h字段设置失败", e);
        }
    }

    // 动态查找字段的辅助方法（findStringFieldByValue、findIntFieldByValue等）
    private Field findStringFieldByValue(Class<?> clazz, Object instance, String testValue) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() != String.class) continue;
                field.setAccessible(true);
                Object original = field.get(instance);
                field.set(instance, testValue);
                if (testValue.equals(field.get(instance))) {
                    field.set(instance, original);
                    return field;
                }
                field.set(instance, original);
            }
        } catch (Exception e) {
            Log.e(TAG, "查找String字段失败", e);
        }
        return null;
    }

    private Field findIntFieldByValue(Class<?> clazz, Object instance, int testValue) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() != int.class && field.getType() != Integer.class) continue;
                field.setAccessible(true);
                Object original = field.get(instance);
                field.set(instance, testValue);
                if (testValue == ((Number) field.get(instance)).intValue()) {
                    field.set(instance, original);
                    return field;
                }
                field.set(instance, original);
            }
        } catch (Exception e) {
            Log.e(TAG, "查找int字段失败", e);
        }
        return null;
    }

    private Field findLongFieldByValue(Class<?> clazz, Object instance, long testValue) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() != long.class && field.getType() != Long.class) continue;
                field.setAccessible(true);
                Object original = field.get(instance);
                field.set(instance, testValue);
                if (testValue == ((Number) field.get(instance)).longValue()) {
                    field.set(instance, original);
                    return field;
                }
                field.set(instance, original);
            }
        } catch (Exception e) {
            Log.e(TAG, "查找long字段失败", e);
        }
        return null;
    }

    private Field findBooleanFieldByValue(Class<?> clazz, Object instance, boolean testValue) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.getType() != boolean.class && field.getType() != Boolean.class) continue;
                field.setAccessible(true);
                Object original = field.get(instance);
                field.set(instance, testValue);
                if (testValue == ((Boolean) field.get(instance)).booleanValue()) {
                    field.set(instance, original);
                    return field;
                }
                field.set(instance, original);
            }
        } catch (Exception e) {
            Log.e(TAG, "查找boolean字段失败", e);
        }
        return null;
    }

    private boolean sendViaEventBus(Object event) {
        try {
            String[] eventBusClassNames = {
                    "com.tencent.mm.kernel.a",
                    "com.tencent.mm.sdk.b.a",
                    "com.tencent.mm.framework.eventbus.a"
            };
            for (String className : eventBusClassNames) {
                try {
                    Class<?> eventBusClass = Class.forName(className, true, tinkerClassLoader);
                    Object eventBus = XposedHelpers.callStaticMethod(eventBusClass, "gf");
                    XposedHelpers.callMethod(eventBus, "post", event);
                    Log.d(TAG, "通过事件总线" + className + "发送成功");
                    return true;
                } catch (Exception e) {
                    Log.w(TAG, "事件总线" + className + "发送失败", e);
                }
            }
            return false;
        } catch (Exception e) {
            Log.e(TAG, "事件总线发送异常", e);
            return false;
        }
    }

    private boolean verifyVideoUploadResult(Object i8Msg, int retryCount) {
        if (retryCount >= 5) {
            Log.e(TAG, "视频CDN上传重试次数耗尽，判定失败");
            return false;
        }
        try {
            long msgId = 0;
            for (String methodName : I8_GET_MSG_ID_METHODS) {
                try {
                    msgId = (long) XposedHelpers.callMethod(i8Msg, methodName);
                    break;
                } catch (NoSuchMethodError e) {
                    continue;
                }
            }
            if (msgId == 0) {
                Log.e(TAG, "获取i8消息ID失败，无法校验上传状态");
                return false;
            }

            Class<?> v20nClass = Class.forName(CLASS_V20_N, true, tinkerClassLoader);
            Object v20nInstance = XposedHelpers.newInstance(v20nClass);
            Class<?> i20jClass = Class.forName(CLASS_I20_J, true, tinkerClassLoader);
            Object i20jInstance = XposedHelpers.newInstance(i20jClass);
            XposedHelpers.setObjectField(i20jInstance, "f248663b", String.valueOf(msgId));

            ArrayList<?> statusList = (ArrayList<?>) XposedHelpers.callMethod(v20nInstance, "zb", i20jInstance);
            if (statusList != null && !statusList.isEmpty()) {
                int status = (int) statusList.get(0);
                if (status == 2) {
                    Log.d(TAG, "视频CDN上传成功：msgId=" + msgId + "，状态码=" + status);
                    return true;
                } else if (status == 1) {
                    Log.w(TAG, "视频CDN上传中，第" + (retryCount + 1) + "次重试...");
                    Thread.sleep(3000);
                    return verifyVideoUploadResult(i8Msg, retryCount + 1);
                } else {
                    Log.e(TAG, "视频CDN上传失败：状态码=" + status);
                    return false;
                }
            }
            Log.w(TAG, "未查询到上传状态，第" + (retryCount + 1) + "次重试...");
            Thread.sleep(3000);
            return verifyVideoUploadResult(i8Msg, retryCount + 1);
        } catch (Exception e) {
            Log.e(TAG, "校验视频上传结果失败", e);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
            return verifyVideoUploadResult(i8Msg, retryCount + 1);
        }
    }

    private void verifyVideoUploadOnMainThread(Object i8Msg) {
        try {
            Class<?> handlerClass = Class.forName("com.tencent.mm.kernel.g", true, tinkerClassLoader);
            Object mainHandler = XposedHelpers.callStaticMethod(handlerClass, "getMainThreadHandler");

            Runnable verifyRunnable = () -> verifyVideoUploadResult(i8Msg, 0);
            XposedHelpers.callMethod(mainHandler, "post", verifyRunnable);
            Log.d(TAG, "视频上传校验已投递到微信主线程");
        } catch (Exception e) {
            Log.e(TAG, "获取主线程Handler失败，回退到子线程校验");
            new Thread(() -> verifyVideoUploadResult(i8Msg, 0)).start();
        }
    }

    private String generateRandomSignature() {
        return UUID.randomUUID().toString().replace("-", "").toLowerCase();
    }

    private byte[] bitmapToByteArray(Bitmap bitmap, boolean recycle) {
        if (bitmap == null) {
            Log.w(TAG, "空Bitmap转字节数组");
            return new byte[0];
        }

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, os);
        byte[] result = os.toByteArray();

        try {
            os.close();
        } catch (IOException e) {
            Log.e(TAG, "ByteArrayOutputStream关闭失败", e);
        }

        if (recycle && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        return result;
    }


    private void copyFile(File source, File dest) throws IOException {
        try (InputStream in = new FileInputStream(source);
             OutputStream out = new FileOutputStream(dest)) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
    }

    private Class<?> findClassWithFallback(String className, ClassLoader classLoader) {
        try {
            return XposedHelpers.findClass(className, classLoader);
        } catch (XposedHelpers.ClassNotFoundError e) {
            if (className.contains("$")) {
                String fallbackName = className.replace("$", ".");
                try {
                    return XposedHelpers.findClass(fallbackName, classLoader);
                } catch (Exception e2) {
                    Log.w(TAG, "找不到类（备选格式）: " + fallbackName);
                }
            }
            Log.w(TAG, "找不到类: " + className);
            return null;
        }
    }
}